﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Drawing.Printing;
using System.Security.Principal;

using SsrsRenderStudio.Shared;
using SsrsRenderStudio.KnownTypesAndModels;

namespace SsrsRenderStudio.Configuration
{
    public static class ConfigurationManager
    {
        private static readonly string LOCAL_APPLICATION_CONFIG = ConfigurationManager.GetCurrentDirectory() + @"\application.config";

        public static readonly string MASTER_CONFIGURATION_CACHED_FILE = "master.cache";
        public static readonly string MASTER_CONFIGURATION_CACHED_FILE_TEMP = "master.cache.temp";

        //private static readonly string XPATH_SERVER_CONFIGURATION_SEED = "/configuration/server";
        //private static readonly string XPATH_LOGGING_CONFIGURATION_SEED = "/configuration/logging";

        private static readonly string XPATH_APPLICATION_CONFIGURATION_SEED = "/configuration/application";

        private static readonly string XPATH_SERVER_SMTP_CONFIG = "/configuration/Smtp";
        private static readonly string FORMAT_GET_SERVER_BY_FRIENDLY_NAME = "/configuration/Servers/Server[@FriendlyName='{0}']";
        private static readonly string FORMAT_GET_CLIENT_BY_FRIENDLY_NAME = "/configuration/Clients/Client[@FriendlyName='{0}']";
        private static readonly string FORMAT_GET_ENDPOINT_POINTER_BY_FRIENDLY_NAME = "/configuration/Endpoints/Endpoint[@FriendlyName='{0}']";
        private static readonly string FORMAT_GET_PRINTER_BY_FRIENDLY_NAME = "/configuration/Printers/Printer[@FriendlyName='{0}']";
        private static readonly string FORMAT_GET_REPORT_BY_FRIENDLY_NAME = "/configuration/Reports/Report[@FriendlyName='{0}']";
        private static readonly string FORMAT_GET_REPORTS_ALL = "/configuration/Reports";
        private static readonly string FORMAT_GET_REX_BY_FRIENDLY_NAME = "/configuration/ReportServers/ReportServer[@FriendlyName='{0}']";
        //private static readonly string FORMAT_GET_SERVER_LOGGING_ACTIONS_BY_FRIENDLY_NAME_AND_SEVERITY = "/configuration/Servers/Server[@FriendlyName='{0}']/Logging/Actions/Action[contains(@Severity,'{1}')]";
        private static readonly string FORMAT_GET_LOGGING_ACTIONS_BY_FRIENDLY_NAME_AND_SEVERITY = "/configuration/Logging/Log[@FriendlyName='{0}']/Actions/Action[contains(@Severity,'{1}')]";
        private static readonly string FORMAT_GET_LOGGING_CONFIG_BY_FRIENDLY_NAME = "/configuration/Logging/Log[@FriendlyName='{0}']";

        private static readonly string XPATH_RECOVERY_REX = "/configuration/Recovery/RexRecovery/RecoverOn";

        public static readonly string RENDER_STUDIO_SERVICE_NAME = "SsrsRenderStudioServer";

        public static string GetCurrentDirectory()
        {
            return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(ConfigurationManager)).CodeBase).Replace(@"file:\","");
        }

        #region Remote Master Configuration operations

        /// <summary>
        /// Forces the cached file to be redownloaded on next call to master config.
        /// </summary>
        public static void Reset()
        {
            _MasterConfigurationFileWasCached = true;
        }

        private static bool _MasterConfigurationFileWasCached = false;

        /// <summary>
        /// Downloads a cached copy of the remote master configuration file and returns the URL to the remote master config file.
        /// </summary>
        private static string _MasterConfigurationFileLive
        {
            get
            {
                try
                {
                    // download a cache if empty
                    if (! _MasterConfigurationFileWasCached)
                    {
                        System.Net.WebClient client = new System.Net.WebClient();

                        try
                        {
                            //client.DownloadFile(ServerConfigFromConfigPointer().RemoteConfiguration, ConfigurationManager.GetCurrentDirectory() + @"\" + MASTER_CONFIGURATION_CACHED_FILE_TEMP);
                            client.DownloadFile(ApplicationConfigFromConfigPointer.RemoteMasterConfiguration, ConfigurationManager.GetCurrentDirectory() + @"\" + MASTER_CONFIGURATION_CACHED_FILE_TEMP);
                            System.IO.File.Copy(ConfigurationManager.GetCurrentDirectory() + @"\" + MASTER_CONFIGURATION_CACHED_FILE_TEMP, ConfigurationManager.GetCurrentDirectory() + @"\" + MASTER_CONFIGURATION_CACHED_FILE, true);
                            System.IO.File.Delete(ConfigurationManager.GetCurrentDirectory() + @"\" + MASTER_CONFIGURATION_CACHED_FILE_TEMP);
                             Logger.Log(EnumLoggingSeverity.INFORMATIONAL, "ConfigurationManager: " + "Caching " + ApplicationConfigFromConfigPointer.RemoteMasterConfiguration);

                            _MasterConfigurationFileWasCached = true;

                            try { System.IO.File.Delete(ConfigurationManager.GetCurrentDirectory() + @"\_MASTER_CONFIGURATION_NOT_AVAILABLE.FAILURE");}
                            catch { }
                        }
                        catch (Exception ex)
                        {
                             Logger.Log(EnumLoggingSeverity.CRITICAL, "ConfigurationManager: " + "Caching " + ApplicationConfigFromConfigPointer.RemoteMasterConfiguration + " failed!\r\nException: " + ex.Message);
                             Shared.Logger.WriteConsole("MASTER CONFIGURATION NOT AVAILABLE!");
                             try { System.IO.File.Create(ConfigurationManager.GetCurrentDirectory() + @"\_MASTER_CONFIGURATION_NOT_AVAILABLE.FAILURE"); }
                             catch { }

                        }  
                    }

                    return ApplicationConfigFromConfigPointer.RemoteMasterConfiguration;
                    //return ServerConfigFromConfigPointer().RemoteConfiguration;
                }
                catch
                {
                    return string.Empty;
                }
            }
        }

        /// <summary>
        /// Retrieves a single XML node from XPATH.
        /// </summary>
        private static XmlNode GetSingleNodeFromMasterConfigurationFile(string xpath)
        {
             Logger.Log(EnumLoggingSeverity.DEBUG, "ConfigurationManager: " + "XML Load : " + _MasterConfigurationFileLive);
             Logger.Log(EnumLoggingSeverity.DEBUG, "ConfigurationManager: " + "XML Query: " + xpath);
            XmlDocument xmlDoc = new XmlDocument();

            try
            {
                //DummyLogger.Log("getNodeFromMaster: loading remote master");
                // we always try to load the live remote master config first...
                xmlDoc.Load(_MasterConfigurationFileLive);
            }
            catch (Exception ex)
            {
                Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Failed to load live master configuration, attempting cached copy.");
                if (System.IO.File.Exists(ConfigurationManager.GetCurrentDirectory() + @"\" + MASTER_CONFIGURATION_CACHED_FILE))
                    xmlDoc.Load(ConfigurationManager.GetCurrentDirectory() + @"\" + MASTER_CONFIGURATION_CACHED_FILE);
                else
                {
                    Logger.Log(EnumLoggingSeverity.CRITICAL, "ConfigurationManager: " + "Cached copy of master configuration does not exist.");
                    return null;
                }
            }

            return xmlDoc.SelectSingleNode(xpath); // TODO: look into case insensitive
        }

        /// <summary>
        /// Retrieve multiple nodes.
        /// </summary>
        private static XmlNodeList GetMultipleNodesFromMasterConfigurationFile(string xpath)
        {
             Logger.Log(EnumLoggingSeverity.DEBUG, "ConfigurationManager: " + "XML Load : " + _MasterConfigurationFileLive);
             Logger.Log(EnumLoggingSeverity.DEBUG, "ConfigurationManager: " + "XML Query: " + xpath);
            XmlDocument xmlDoc = new XmlDocument();

            try
            {
                // we always try to load the live remote master config first...
                xmlDoc.Load(_MasterConfigurationFileLive);
            }
            catch (Exception ex)
            {
                 Logger.Log(EnumLoggingSeverity.CRITICAL, "ConfigurationManager: " + "Failed to load live master configuration, attempting cached copy.");
                if (System.IO.File.Exists(ConfigurationManager.GetCurrentDirectory() + @"\" + MASTER_CONFIGURATION_CACHED_FILE))
                    xmlDoc.Load(ConfigurationManager.GetCurrentDirectory() + @"\" + MASTER_CONFIGURATION_CACHED_FILE);
                else
                {
                     Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Cached copy of master configuration does not exist.");
                    return null;
                }
            }

            return xmlDoc.SelectNodes(xpath); // TODO: look into case insensitive
        }

        #endregion

        #region Local Application.Config operations

        private static XmlNode GetSingleNodeFromApplicationConfigurationFile(string xpath)
        {
            XmlDocument xmlDoc = new XmlDocument();

            try
            {
                xmlDoc.Load(LOCAL_APPLICATION_CONFIG);
            }
            catch (Exception ex)
            {
                return null;
            }

            return xmlDoc.SelectSingleNode(xpath); // TODO: look into case insensitive
        }

        #endregion

        #region  local config.

        /*
        // DEPRECATED: replaced by ApplicationConfig property to allow clients the use of the remote master config file.
        private static ServerConfig _serverInfoSeed = null;
        public static ServerConfig ServerConfigFromConfigPointer()
        {
            if (_serverInfoSeed != null) return _serverInfoSeed;

            XmlNode node = GetSingleNodeFromApplicationConfigurationFile(XPATH_SERVER_CONFIGURATION_SEED);

            if (node == null)
                return null;

            _serverInfoSeed = new ServerConfig((string)node.Attributes["name"].Value,
                (string)node.Attributes["configuration"].Value);
            
            return _serverInfoSeed;
        }
        */

        //private static ApplicationConfig _applicationInfoSeed = null;
        /// <summary>
        /// Information about this application from its local application.config.
        /// </summary>
        public static ApplicationConfig ApplicationConfigFromConfigPointer
        {
            get
            {
                //if (_applicationInfoSeed != null) return _applicationInfoSeed;

                XmlNode node = GetSingleNodeFromApplicationConfigurationFile(XPATH_APPLICATION_CONFIGURATION_SEED);
                //TODO: it would be nice to notify logger if any config change on the fly...

                if (node == null) return null;

                return new ApplicationConfig(
                    node.Attributes["type"].Value,
                    node.Attributes["name"].Value,
                    node.Attributes["configuration"].Value);

                //return _applicationInfoSeed;
            }
        }

        /*
        public static LoggingConfig LoggingConfigFromConfigPointer()
        {
            XmlNode node =  GetSingleNodeFromApplicationConfigurationFile(XPATH_LOGGING_CONFIGURATION_SEED);

            if (node == null)
                return null;

            LoggingConfig logging = new LoggingConfig((string)node.Attributes["source"].Value,
                int.Parse(node.Attributes["verbosity"].Value));

            return logging;
        }
        */

        #endregion

        #region SsrsRenderStudio Configuration from master config.

        /// <summary>
        /// Retrieve SMTP settings.
        /// </summary>
        public static SmtpInfo SmtpSettings()
        {
            XmlNode node = GetSingleNodeFromMasterConfigurationFile(XPATH_SERVER_SMTP_CONFIG);

            if (node == null)
                return null;

            SmtpInfo info = new SmtpInfo((string)node.Attributes["Host"].Value,
                (string)node.Attributes["FromAddress"].Value,
                (string)node.Attributes["FromDisplayName"].Value,
                (string)node.Attributes["AdministratorEmail"].Value);

            return info;
        }

        public static LoggingConfig LoggingConfigForSource(string sourceName)
        {
            XmlNode node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_LOGGING_CONFIG_BY_FRIENDLY_NAME, sourceName));
            if (node == null)
            {
                Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Requested Logging Configuration Not Found: " + sourceName);
                node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_LOGGING_CONFIG_BY_FRIENDLY_NAME, "DEFAULT"));
            }
            if (node == null) return null;

            LoggingConfig info = new LoggingConfig(
                node.Attributes["FriendlyName"].Value,
                int.Parse(node.Attributes["Verbosity"].Value),
                bool.Parse(node.Attributes["IsImmediate"].Value),
                TimeSpan.Parse(node.Attributes["LogProcessingInterval"].Value));

            return info;
        }

        /// <summary>
        /// Retrieve logging actions for a particular source.
        /// </summary>
        public static List<LoggingAction> LoggingActionsForSource(string sourceName, EnumLoggingSeverity severity)
        {
            List<LoggingAction> actions = new List<LoggingAction>();

            XmlNodeList nodes = GetMultipleNodesFromMasterConfigurationFile(string.Format(FORMAT_GET_LOGGING_ACTIONS_BY_FRIENDLY_NAME_AND_SEVERITY, sourceName, (int)severity));
            if (nodes != null)
            {
                foreach (XmlNode n in nodes)
                {
                    foreach (XmlNode child in n.ChildNodes)
                    {
                        string[] parameters = new string[(child.Attributes.Count -1 ) *2]; // make it Count sized, we don't want Type attrib in there
                        int i = 0;
                        foreach (XmlAttribute att in child.Attributes)
                        {
                            if (att.Name != "Type")
                            {
                                parameters[i] = att.Name;
                                parameters[i + 1] = att.Value;
                                i += 2;
                            }
                        }

                        actions.Add(new LoggingAction(
                            severity,
                            (EnumLoggingAction)Enum.Parse(typeof(EnumLoggingAction), child.Attributes["Type"].Value),
                            new ParameterHash(parameters)));
                    }
                }
            }

            return actions;
        }

        /// <summary>
        /// Return URL pointer to the remote endpoint config file.
        /// </summary>
        public static EndpointConfig EndpointConfigForApplication(string friendlyEndpointName)
        {
            XmlNode node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_ENDPOINT_POINTER_BY_FRIENDLY_NAME, friendlyEndpointName));
            if (node == null)
            {
                Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Requested Endpoint Configuration Not Found: " + friendlyEndpointName);
                node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_ENDPOINT_POINTER_BY_FRIENDLY_NAME, friendlyEndpointName));
            }
            if (node == null) return null;

            EndpointConfig info = new EndpointConfig(
                node.Attributes["FriendlyName"].Value,
                node.Attributes["Location"].Value);

            return info;
        }

        //deprecated: 12/05/11
        /*
        public static ServerInfo ServerInfoFromFriendlyName(string friendlyServerName)
        {
            XmlNode node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_SERVER_BY_FRIENDLY_NAME, friendlyServerName));
            if (node == null)
            {
                Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Requested Server Configuration Not Found: " + friendlyServerName);
                node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_SERVER_BY_FRIENDLY_NAME, "DEFAULT"));
            }
            if (node == null) return null;

            ServerInfo info = new ServerInfo(
                node.Attributes["FriendlyName"].Value,
                node.Attributes["Logging"].Value,
                node.Attributes["Endpoint"].Value,
                node.Attributes["ServiceName"].Value,
                node.Attributes["PreferredReportServer"].Value,
                node.Attributes["PreferredReportServerGroup"].Value,
                bool.Parse(node.Attributes["PreferredReportServerGroupOnly"].Value),
                TimeSpan.Parse(node.Attributes["ReportCacheTtl"].Value),
                TimeSpan.Parse(node.Attributes["RexKeepAliveTimer"].Value));

            info.RexRecoveryOnException = RexRecoveryOnExceptions();

            return info;
        }
        */

        /// <summary>
        /// Retrieve SsrsRenderServer configuration.
        /// </summary>
        public static RenderStudioServerConfig10 ServerInfoFromFriendlyName10(string friendlyServerName)
        {
            XmlNode node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_SERVER_BY_FRIENDLY_NAME, friendlyServerName));
            if (node == null)
            {
                Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Requested Server Configuration Not Found: " + friendlyServerName);
                node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_SERVER_BY_FRIENDLY_NAME, "DEFAULT"));
            }
            if (node == null) return null;

            RenderStudioServerConfig10 info = new RenderStudioServerConfig10
            {
                FriendlyName = node.Attributes["FriendlyName"].Value,
                LoggingSource = node.Attributes["Logging"].Value,
                EndpointPointer = node.Attributes["Endpoint"].Value,
                ServiceName = node.Attributes["ServiceName"].Value,
                PreferredReportServer = node.Attributes["PreferredReportServer"].Value,
                PreferredReportServerGroup = node.Attributes["PreferredReportServerGroup"].Value,
                PreferredReportServerGroupOnly =  bool.Parse(node.Attributes["PreferredReportServerGroupOnly"].Value),
                ReportCacheTtl = TimeSpan.Parse(node.Attributes["ReportCacheTtl"].Value),
                RexKeepAliveTimer = TimeSpan.Parse(node.Attributes["RexKeepAliveTimer"].Value),
                IsDebug = bool.Parse(node.Attributes["IsDebug"].Value)
            };

            info.RexRecoveryOnException = RexRecoveryOnExceptions();

            return info;
        }

        //deprecated : 12/05/11
        /*
        public static ClientInfo ClientInfoFromFriendlyName(string friendlyClientName)
        {
            XmlNode node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_CLIENT_BY_FRIENDLY_NAME, friendlyClientName));
            if (node == null)
            {
                Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Requested Client Configuration Not Found: " + friendlyClientName);
                node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_CLIENT_BY_FRIENDLY_NAME, "DEFAULT"));
            }
            if (node == null) return null;

            return new ClientInfo(
                node.Attributes["FriendlyName"].Value,
                node.Attributes["Logging"].Value,
                node.Attributes["Endpoint"].Value);
        }
        */

        public static RenderStudioClientConfig10 ClientInfoFromFriendlyName10(string friendlyClientName)
        {
            XmlNode node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_CLIENT_BY_FRIENDLY_NAME, friendlyClientName));
            if (node == null)
            {
                Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Requested Client Configuration Not Found: " + friendlyClientName);
                node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_CLIENT_BY_FRIENDLY_NAME, "DEFAULT"));
            }
            if (node == null) return null;

            return new RenderStudioClientConfig10
            {
                FriendlyName = node.Attributes["FriendlyName"].Value,
                LoggingSource = node.Attributes["Logging"].Value,
                EndpointPointer = node.Attributes["Endpoint"].Value
            };
        }

        private static List<EnumServiceSideException> RexRecoveryOnExceptions()
        {
            List<EnumServiceSideException> recovery = new List<EnumServiceSideException>();

            /**** REX Recovery Information ****/
            XmlNodeList nodes = GetMultipleNodesFromMasterConfigurationFile(XPATH_RECOVERY_REX);
            
            if (nodes != null)
            {
                foreach (XmlNode n in nodes)
                {
                    if ((Convert.ToBoolean(n.Attributes["Enabled"].Value)))
                    {
                        try
                        {
                            recovery.Add((EnumServiceSideException)Enum.Parse(typeof(EnumServiceSideException), n.Attributes["Exception"].Value));
                        }
                        catch { }
                    }
                }
            }
            else
            {
                //TODO: should recovery be built in for some default exceptions?
            }

            return recovery;
        }

        //deprecated 12/05/11
        /*
        public static ReportServerInfo ReportServerSettingsFromFriendlyName(string friendlyRsName)
        {
            XmlNode node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_REX_BY_FRIENDLY_NAME, friendlyRsName));
            if (node == null)
            {
                Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Requested REX Configuration Not Found: " + friendlyRsName);
                node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_REX_BY_FRIENDLY_NAME, "DEFAULT"));
            }
            if (node == null) return null;

            ReportServerInfo settings = new ReportServerInfo((string)node.Attributes["FriendlyName"].Value,
                (string)node.Attributes["Group"].Value,
                (string)node.Attributes["RexUrl"].Value,
                (string)node.Attributes["RsUrl"].Value);

            return settings;
        }
        */

        /// <summary>
        /// Retrieve SQL Reporting Services configuration such as REX and RS web service URLs.
        /// </summary>
        public static ReportServerConfig10 ReportServerSettingsFromFriendlyName(string friendlyRsName)
        {
            XmlNode node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_REX_BY_FRIENDLY_NAME, friendlyRsName));
            if (node == null)
            {
                Logger.Log(EnumLoggingSeverity.ALERT, "ConfigurationManager: " + "Requested REX Configuration Not Found: " + friendlyRsName);
                node = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_REX_BY_FRIENDLY_NAME, "DEFAULT"));
            }
            if (node == null) return null;

            ReportServerConfig10 settings = new ReportServerConfig10
                {
                  FriendlyName =  (string)node.Attributes["FriendlyName"].Value,
                  GroupName = (string)node.Attributes["Group"].Value,
                  RexUrl = (string)node.Attributes["RexUrl"].Value,
                  RsUrl = (string)node.Attributes["RsUrl"].Value
                };

            return settings;
        }

        
        /// <summary>
        /// Returns the next REX from the config.
        /// </summary>
        /// <param name="ReportServersToOmit">Exclude these REXs from search.</param>
        /// <param name="preferredGroup">Group name.</param>
        /// <param name="preferredGroupOnly">Search only pref group?</param>
        /// <returns></returns>
        public static ReportServerConfig10 NextReportServerSettings(System.Collections.ArrayList ReportServersToOmit, string preferredGroup, bool preferredGroupOnly)
        {
            string x1 = "/configuration/ReportServers/ReportServer[@FriendlyName='{0}']/following-sibling::*[1]";

            //
            //TODO: find a way to use XPATH and passing in only the last rex used
            //  try comparing node positions and stepping through
            //
            
            string FORMAT_GET_REXS_BY_GROUP_AND_OMIT_SELECT_REXS = "/configuration/ReportServers/ReportServer[{0}{1}]";

            /*  build rexs to omit */
            // we take a list of report server friendly names that were already used
            //  and build a string fn!=abc or fn!=xyz
            //  to exclude those rexs from xpath selection
            string OMIT_FRIENDLY_NAME_TEMPLATE = "@FriendlyName!='{0}'";
            string OMIT_REXS_CONCAT = "(";
            string SPECIFY_PREF_GRP = "";
            int i = 0;

            foreach (string s in ReportServersToOmit)
            {
                if (i > 0) OMIT_REXS_CONCAT += " and ";
                OMIT_REXS_CONCAT += string.Format(OMIT_FRIENDLY_NAME_TEMPLATE, s);
                i++;
            }
            OMIT_REXS_CONCAT += ")";
            /*  end build rexs to omit */

            /* build pref group */
            // then we check if the server only works with preferred rexs
            //  and build the xpath to search that group only
            if (preferredGroupOnly)
                SPECIFY_PREF_GRP = string.Format("and @Group='{0}'", preferredGroup);
            /* end build pref group */

            XmlNodeList nodes = GetMultipleNodesFromMasterConfigurationFile(string.Format(FORMAT_GET_REXS_BY_GROUP_AND_OMIT_SELECT_REXS, OMIT_REXS_CONCAT, SPECIFY_PREF_GRP));
            
            if (nodes == null || nodes.Count == 0)
                return null;

            ReportServerConfig10 settings = new ReportServerConfig10
            {
                FriendlyName = (string)nodes[0].Attributes["FriendlyName"].Value,
                GroupName = (string)nodes[0].Attributes["Group"].Value,
                RexUrl = (string)nodes[0].Attributes["RexUrl"].Value,
                RsUrl = (string)nodes[0].Attributes["RsUrl"].Value
            };

            return settings;
        }

        public static ReportServerConfig10 NextReportServerSettings(string currentReportServer, string preferredGroup, bool preferredGroupOnly)
        {
            string x1 = "/configuration/ReportServers/ReportServer[@FriendlyName='{0}']/following-sibling::*[1]";
            string x2 = "";

            if (preferredGroupOnly)
                x2 = string.Format("[@Group='{0}']", preferredGroup);

            XmlNode node = GetSingleNodeFromMasterConfigurationFile(string.Format(x1 + x2, currentReportServer));

            if (node == null)
                return null;

            ReportServerConfig10 settings = new ReportServerConfig10
            {
                FriendlyName = (string)node.Attributes["FriendlyName"].Value,
                GroupName = (string)node.Attributes["Group"].Value,
                RexUrl = (string)node.Attributes["RexUrl"].Value,
                RsUrl = (string)node.Attributes["RsUrl"].Value
            };

            return settings;
        }

        //deprecated 12/05/11
        /*
        public static PrinterInfo PrinterSettingsFromFriendlyName(string friendlyPrinterName)
        {
            XmlNode printerNode = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_PRINTER_BY_FRIENDLY_NAME, friendlyPrinterName));

            if (printerNode == null) return null;

            PrinterInfo settings = new PrinterInfo((string)printerNode.Attributes["FriendlyName"].Value,
                (string)printerNode.Attributes["NetworkDestination"].Value,
                bool.Parse(printerNode.Attributes["OverridePrinterSettings"].Value),
                int.Parse(printerNode.Attributes["DpiX"].Value),
                int.Parse(printerNode.Attributes["DpiY"].Value));

            if (!settings.OverrideSettings)
            {
                //using (WindowsImpersonationContext wic = WindowsIdentity.Impersonate(IntPtr.Zero))
                //{
                    PrinterSettings printerSettings = new PrinterSettings();
                    printerSettings.PrinterName = settings.NetworkDestination;
                    
                    if (printerSettings.IsValid)
                    {
                        settings.DpiX = printerSettings.DefaultPageSettings.PrinterResolution.X;
                        settings.DpiY = printerSettings.DefaultPageSettings.PrinterResolution.Y;
                    }
                    else
                    {
                        // if printer is not valid this means that the printer is not available.
                        return null;
                    }

                //   wic.Undo();
                //}
            }

            return settings;
        }
        */

        /// <summary>
        /// Retrieve printer configuration.
        /// </summary>
        public static PrinterInfo10 PrinterSettingsFromFriendlyName10(string friendlyPrinterName)
        {
            XmlNode printerNode = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_PRINTER_BY_FRIENDLY_NAME, friendlyPrinterName));

            if (printerNode == null) return null;

            PrinterInfo10 settings = new PrinterInfo10
                {
                    FriendlyName = (string)printerNode.Attributes["FriendlyName"].Value,
                    Description = (string)printerNode.Attributes["FriendlyName"].Value,
                    NetworkDestination = (string)printerNode.Attributes["NetworkDestination"].Value,
                    OverrideSettings = bool.Parse(printerNode.Attributes["OverridePrinterSettings"].Value),
                    DpiX = int.Parse(printerNode.Attributes["DpiX"].Value),
                    DpiY = int.Parse(printerNode.Attributes["DpiY"].Value),
                    CacheDeviceContext = bool.Parse(printerNode.Attributes["CacheDeviceContext"].Value),
                    DeviceContextTTL = TimeSpan.Parse(printerNode.Attributes["DeviceContextTTL"].Value)
                };

            //cmm TODO: this could be sped up with using win32 GetDeviceCaps()
            if (!settings.OverrideSettings)
            {
                //using (WindowsImpersonationContext wic = WindowsIdentity.Impersonate(IntPtr.Zero))
                //{
                PrinterSettings printerSettings = new PrinterSettings();
                printerSettings.PrinterName = settings.NetworkDestination;

                if (printerSettings.IsValid)
                {
                    settings.DpiX = printerSettings.DefaultPageSettings.PrinterResolution.X;
                    settings.DpiY = printerSettings.DefaultPageSettings.PrinterResolution.Y;
                }
                else
                {
                    // if printer is not valid this means that the printer is not available.
                    return null;
                }

                //   wic.Undo();
                //}
            }

            return settings;
        }



        // deprecated 12/05/11
        /*
        public static ReportInfo ReportSettingsFromFriendlyName(string friendlyReportName)
        {
            XmlNode reportNode = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_REPORT_BY_FRIENDLY_NAME, friendlyReportName));

            if (reportNode == null) return null;

            ReportInfo settings = new ReportInfo((string)reportNode.Attributes["FriendlyName"].Value,
                (string)reportNode.Attributes["ReportUrl"].Value,
                bool.Parse(reportNode.Attributes["OverridePageSettings"].Value),
                Double.Parse(reportNode.Attributes["Width"].Value),
                Double.Parse(reportNode.Attributes["Height"].Value),
                Double.Parse(reportNode.Attributes["MarginTop"].Value),
                Double.Parse(reportNode.Attributes["MarginBottom"].Value),
                Double.Parse(reportNode.Attributes["MarginRight"].Value),
                Double.Parse(reportNode.Attributes["MarginLeft"].Value));

            return settings;
        }
        */

        /// <summary>
        /// Retrieve report configuration.
        /// </summary>
        public static ReportConfig10 ReportSettingsFromFriendlyName10(string friendlyReportName)
        {
            XmlNode reportNode = GetSingleNodeFromMasterConfigurationFile(string.Format(FORMAT_GET_REPORT_BY_FRIENDLY_NAME, friendlyReportName));

            if (reportNode == null) return null;

            ReportConfig10 report = new ReportConfig10
                                    {
                                        FriendlyName = (string)reportNode.Attributes["FriendlyName"].Value,
                                        Url = (string)reportNode.Attributes["ReportUrl"].Value,
                                        OverridePageSettings = bool.Parse(reportNode.Attributes["OverridePageSettings"].Value),
                                        PageSettings = new PageSettings10
                                        {
                                            Width = Double.Parse(reportNode.Attributes["Width"].Value),
                                            Height = Double.Parse(reportNode.Attributes["Height"].Value),
                                            MarginTop = Double.Parse(reportNode.Attributes["MarginTop"].Value),
                                            MarginBottom = Double.Parse(reportNode.Attributes["MarginBottom"].Value),
                                            MarginRight = Double.Parse(reportNode.Attributes["MarginRight"].Value),
                                            MarginLeft = Double.Parse(reportNode.Attributes["MarginLeft"].Value)
                                            //,Rotation = Double.Parse(reportNode.Attributes["Rotation"].Value)
                                        }
                                    };

            return report;
        }

        public static List<ReportConfig10> ReportSettingsGetAll10()
        {
            XmlNodeList nodes = GetMultipleNodesFromMasterConfigurationFile(FORMAT_GET_REPORTS_ALL);
            List<ReportConfig10> reports = new List<ReportConfig10>();

            if (nodes != null)
            {
                foreach (XmlNode node in nodes)
                {
                    foreach (XmlNode reportNode in node.ChildNodes)
                    {
                        reports.Add(new ReportConfig10
                            {
                                FriendlyName = (string)reportNode.Attributes["FriendlyName"].Value,
                                Url = (string)reportNode.Attributes["ReportUrl"].Value,
                                OverridePageSettings = bool.Parse(reportNode.Attributes["OverridePageSettings"].Value),
                                PageSettings = new PageSettings10
                                {
                                    Width = Double.Parse(reportNode.Attributes["Width"].Value),
                                    Height = Double.Parse(reportNode.Attributes["Height"].Value),
                                    MarginTop = Double.Parse(reportNode.Attributes["MarginTop"].Value),
                                    MarginBottom = Double.Parse(reportNode.Attributes["MarginBottom"].Value),
                                    MarginRight = Double.Parse(reportNode.Attributes["MarginRight"].Value),
                                    MarginLeft = Double.Parse(reportNode.Attributes["MarginLeft"].Value)
                                }
                            });
                    }
                }

                return reports;
            }
            else
            {
                return reports;
            }
        }

        #endregion

        #region oldcode

        /*
        public static readonly string RENDER_STUDIO_USER_ENDPOINT = @"net.tcp://ssrs-render-studio-server.domain.com:8316/RenderStudioUserService";
        public static readonly string RENDER_STUDIO_SYSTEM_ENDPOINT = @"net.tcp://ur09rpt01:8316/RenderStudioSystemService";
        public static readonly string RENDER_STUDIO_SYSTEM_ENDPOINT_INTERNAL = @"net.pipe://ssrs-render-studio-server.domain.com/RenderStudioSystemServiceInternal";
        public static readonly int RENDER_STUDIO_SYSTEM_BINDING_MAX_RECEIVE_SIZE = 1048576;

        public static readonly TimeSpan RENDER_STUDIO_USER_SERVER_OPEN_TIMEOUT = new TimeSpan(0, 0, 15);
        public static readonly TimeSpan RENDER_STUDIO_USER_SERVER_CLOSE_TIMEOUT = new TimeSpan(0, 0, 15);
        //public static readonly TimeSpan RENDER_STUDIO_USER_SERVER_RECEIVE_TIMEOUT = new TimeSpan(0, 0, 0); // session timeout

        public static readonly TimeSpan RENDER_STUDIO_CLIENT_SEND_TIMEOUT = new TimeSpan(0, 1, 0);
        public static readonly TimeSpan RENDER_STUDIO_CLIENT_OPEN_TIMEOUT = new TimeSpan(0, 0, 15);
        public static readonly TimeSpan RENDER_STUDIO_CLIENT_CLOSE_TIMEOUT = new TimeSpan(0, 0, 15);
        //public static readonly TimeSpan RENDER_STUDIO_CLIENT_OPERATION_TIMEOUT = new TimeSpan(0, 1, 0);

        /// <summary>
        /// Creates a usable client proxy.
        /// </summary>
        public static T CreateRenderStudioClientProxy<T>(bool internalConnect = false)
        {
            string eaddress = null;
            Binding binding = null;

            if (typeof(T) == typeof(IRenderStudioUserService))
            {
                eaddress = RENDER_STUDIO_USER_ENDPOINT;
                binding = new NetTcpBinding();
            }
            else if (typeof(T) == typeof(IRenderStudioSystemService))
            {
                if (internalConnect)
                {
                    eaddress = RENDER_STUDIO_SYSTEM_ENDPOINT_INTERNAL;
                    binding = new NetNamedPipeBinding();
                }
                else
                {
                    eaddress = RENDER_STUDIO_SYSTEM_ENDPOINT;
                    binding = new NetTcpBinding();
                }
            }
            else
            {
                return default(T);
            }

            binding.OpenTimeout = RENDER_STUDIO_CLIENT_OPEN_TIMEOUT;
            binding.CloseTimeout = RENDER_STUDIO_CLIENT_CLOSE_TIMEOUT;
            binding.SendTimeout = RENDER_STUDIO_CLIENT_SEND_TIMEOUT;

            EndpointAddress address = new EndpointAddress(new Uri(eaddress), EndpointIdentity.CreateSpnIdentity(@"FallbackTo\NTLM"));
            ChannelFactory<T> channel = new ChannelFactory<T>(binding, address);
            T proxy = channel.CreateChannel();

            //((IContextChannel)proxy).OperationTimeout = RENDER_STUDIO_CLIENT_OPERATION_TIMEOUT;
            
            return proxy;
        }

        /// <summary>
        /// Creates a usable server service host.
        /// </summary>
        /// <typeparam name="T">Interface contract.</typeparam>
        /// <typeparam name="P">Concrete implementation.</typeparam>
        /// <returns>Unopened service host.</returns>
        public static ServiceHost CreateRenderStudioServiceHost<T,P>()
        {

            ServiceHost host = new ServiceHost(typeof(P));

            if (typeof(T) == typeof(IRenderStudioUserService))
            {
                string remoteUserAddress = RENDER_STUDIO_USER_ENDPOINT;
                NetTcpBinding remoteUserBinding = new NetTcpBinding();
                remoteUserBinding.CloseTimeout = RENDER_STUDIO_USER_SERVER_CLOSE_TIMEOUT;
                remoteUserBinding.OpenTimeout = RENDER_STUDIO_USER_SERVER_OPEN_TIMEOUT;
                host.AddServiceEndpoint(typeof(T), remoteUserBinding, remoteUserAddress);
            }
            else if (typeof(T) == typeof(IRenderStudioSystemService))
            {
                string remoteServiceAddress = RENDER_STUDIO_SYSTEM_ENDPOINT;
                NetTcpBinding remoteServiceBinding = new NetTcpBinding();
                remoteServiceBinding.CloseTimeout = RENDER_STUDIO_USER_SERVER_CLOSE_TIMEOUT;
                remoteServiceBinding.OpenTimeout = RENDER_STUDIO_USER_SERVER_OPEN_TIMEOUT;
                host.AddServiceEndpoint(typeof(T), remoteServiceBinding, remoteServiceAddress);

                string internalServiceAddress = RENDER_STUDIO_SYSTEM_ENDPOINT_INTERNAL;
                NetNamedPipeBinding internalServiceBinding = new NetNamedPipeBinding();
                internalServiceBinding.CloseTimeout = RENDER_STUDIO_USER_SERVER_CLOSE_TIMEOUT;
                internalServiceBinding.OpenTimeout = RENDER_STUDIO_USER_SERVER_OPEN_TIMEOUT;
                host.AddServiceEndpoint(typeof(T), internalServiceBinding, internalServiceAddress);
            }

            //binding.CloseTimeout = RENDER_STUDIO_USER_SERVER_CLOSE_TIMEOUT;
            //binding.OpenTimeout = RENDER_STUDIO_USER_SERVER_OPEN_TIMEOUT;
            ////binding.ReceiveTimeout = RENDER_STUDIO_USER_SESSION_TIMEOUT;

            //host.AddServiceEndpoint(typeof(T), binding, eaddress);

            return host;
        }
        */

        #endregion
    }
}
